#!/usr/sbin/rsct/perl5/bin/perl 
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 1999,2002 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
# "@(#)65   1.14   src/rsct/registry/cli/bin/retsrfile.perl, srcli, rsct_rpyxh, rpyxht1f3 2/22/01 16:25:48"
######################################################################
#                                                                    #
# Module: retsrfile                                                  #
#                                                                    #
# Purpose:                                                           #
#   retsrfile - Retrieves a file stored in a System Registry table.  #
#                                                                    #
# Syntax:                                                            #
#   retsrfile [-h][-f][-r][-TV] SR_filename [AIX_filename]           #
#                                                                    #
# Flags:                                                             #
#   -h  help - writes this command's usage statement to stdout       #
#   -f  Force overwrite. Use in conjunction with the -r flag to      #
#       overwrite an existing AIX file with a file stored in the     #
#       System Registry.                                             #
#   -n  Do not restore the file details as stored in the System      #
#       Registry. This includes the modification date and            #
#       permission. Use only with the -r flag.                       #
#   -r  Use in conjunction with AIX_filename operand                 #
#       to save the file retrieved from the System Registry into the #
#       file specified AIX_filename. If no AIX_filename is provided, #
#       the name stored in the System Registry table will be used.   #
#   -T Trace. Writes this command's trace messages to stderr         #
#   -V Verbose. Writes this command's verbose messages to stderr     #
#                                                                    #
# Operands:                                                          #
#   SR_filename         Name of the table in the System Registry     #
#                       where the file to be retrieved is stored.    #
#                       An absolute or relative table name can be    #
#                       used.  If a relative name is used, the       #
#                       CT_SR_HOME environment variable must be set  #
#                       (unless the file exists at the root level.)  #
#                                                                    #
#   AIX_filename        Optional. Use with the -r flag to save the   #
#                       retrieved file to the given AIX_filename.    #
#                       Zero or one of these operands can be         #
#                       specified. A relative or absolute file name  #
#                       can be used.                                 #
#                                                                    #
# Description:                                                       #
#   The retsrfile command displays the contents of the file          #
#   SR_filename previously stored as a table in the System Registry. #
#   By default, the file is displayed to standard out. Optionally the#
#   file can be saved to an AIX file specified by using the [-r ]    #
#   flag.  If an AIX_filename operand is not specified when this     #
#   flag is used, then the AIX filename stored with the file image in#
#   the table is used.                                               #
#                                                                    #
#   By default, the file information stored in the Registry,         #
#   including the permissions, last modification and access date are #
#   restored with the AIX file. To avoid restoring this information, #
#   use the flag combination [-nr ], where `n' suppresses information#
#   restoration. (See the mksrfile man page for details on what stats#
#   are stored.)                                                     #
#                                                                    #
#   If the [-r] flag is specified and the AIX file to be written to  #
#   already exists, the command will complete with an error. Use the #
#   [-f] flag (Force) to overwrite and existing AIX file.            #
#                                                                    #
#   NOTE: Some systems require you to be superuser to change file    #
#   ownership. On these systems you may have to run the chown and    #
#   chgrp commands by hand.                                          #
#                                                                    #
# Exit Values:                                                       #
#   0  SR_CLI_SUCCESS        Command completed successfully.         #
#   1  SR_CLI_REGISTRY_ERROR Command terminated due to an underlying #
#                            System Registry error.                  #
#   2  SR_CLI_ERROR          Command terminated due to an underlying #
#                            error in the command script.            #
#   3  SR_CLI_BAD_OPERAND    Command terminated due to user          #
#                            specifying a bad operand.               #
#   4  SR_CLI_BAD_FLAG       Command terminated due to user          #
#                            specifying an invalid flag.             #
#   5  SR_CLI_USER_ERROR     Command terminated due to a user error. #
#                            For example specifying an undefined     #
#                            file (table) to be returned.            #
#                                                                    #
# Examples:                                                          #
#   1. To display the file data stored in the Registry Table         #
#      /samples/stored_file, enter:                                  #
#         retsrfile /samples/stored_file                             #
#                                                                    #
#   2. To retrieve the file data stored in /samples/stored_file to   #
#      a specified AIX file, enter:                                  #
#         retsrfile -r /samples/stored_file aix_output.file          #
#                                                                    #
#   3. To retrieve the file data stored in /samples/stored_file to   #
#      the filename stored in the System Registry table, overwriting #
#      any existing AIX file of the same name, enter:                #
#        retsrfile -rf /samples/stored_file                          #
#                                                                    #
#   4. To retrieve the file data stored in /samples/stored_file to   #
#      the filename stored in the System Registry table, but not     #
#      restoring the original AIX stats, enter:                      #
#        retsrfile -nr /samples/stored_file                          #
#   Note:  This last example is not recommended practise.            #
#                                                                    #
#--------------------------------------------------------------------#
# Inputs:                                                            #
#   from command line                                                #
#                                                                    #
# Outputs:                                                           #
#   stdout - file data, verbose messages displayed to standard out.  #
#   stderr - error messages displayed to standard error.             #
#                                                                    #
# External Ref:                                                      #
#   Commands: $LSMSG                                                 #
#   Extensions:  CT::SR.pm CT::SRrc.pm CT::CT.pm                     #
#   Perl library routines: Getopts::Std                              #
#   SR cli library:  SR_cli_utils - init_session, isRelative,        #
#                               open_table  printCEMsg               #
#                    SR_cli_rc = CLI return codes                    #
#                                                                    #
# Tab Settings:                                                      #
#   4 and tabs should be expanded to spaces before saving this file. #
#   in vi:  (:set ts=4  and   :%!expand -4)                          #
#                                                                    #
# Change Activity:                                                   #
#   000929 HGJ 38317: Initial delivery.                              #
#                                                                    #
######################################################################

#--------------------------------------------------------------------#
#                                                                    #
# General Program Logic/Flow:                                        #
#                                                                    #
# A: Parse command line - store the SR_filename and AIX_filename     #
#    operands as applicable.                                         #
# B: Initialize session with registry, including setting the         #
#    current directory setting depending on whether an absolute or   #
#    relative table name (SR_filename) was given.                    #
# C: Attempt to open table where the file data is stored. Report an  #
#    error and exit if unsuccessful                                  #
# D: Call CT::SR::get_fields_by_index to retrieve the file data and  #
#    other information stored in the table.                          #
# E: Display information as requested.                               #
# F: Clean up session table and tree.                                #
#                                                                    #
#--------------------------------------------------------------------#


#--------------------------------------------------------------------#
# Included Libraries                                                 #
#--------------------------------------------------------------------#
use lib "/usr/sbin/rsct/pm";
use locale;
use Getopt::Std;
use File::Copy;

use CT::CT qw(:ct_data_type_t);
use CT_cli_utils qw(printIMsg
                    printEMsg
);

use CT::SRrc;
use CT::SR;
use SR_cli_utils qw(init_session    isRelative 
                    printCEMsg      $DEFAULT_GLOBAL_MOUNT_POINT
                    open_table      clean_session
                    error_exit      set_session_variables
);
use SR_cli_rc qw(:return_codes);


#--------------------------------------------------------------------#
# Global Variables                                                   #
#--------------------------------------------------------------------#
# Constants for use throughout the program
$TRUE                   = 1;
$FALSE                  = 0;

# Initialise defaults for output options 
$Opt_Force_Save         = $FALSE;       # see -f flag 
$Opt_Restore_Stats      = $TRUE;        # see -n flag 
$Opt_Save_Result        = $FALSE;       # see -r flag
$Trace                  = $FALSE;       # see -T flag
$Verbose                = $FALSE;       # see -V flag

# Message Map variables
$PROGNAME           = "retsrfile";       # Program Name for messages
$MSGCAT             = "srcli.cat";       # msg catalogue for this cmd
$CTDIR              = "/usr/sbin/rsct";  # Cluster directory path
$CTBINDIR           = "$CTDIR/bin";      # Cluster Bin directory path
$LSMSG              = "$CTBINDIR/ctdspmsg";  # Display message routine
$ENV{'MSGMAPPATH'}  = "$CTDIR/msgmaps";  # Msg map path for $LSMSG  

%Cleanup = ();                           # Hash of items to cleanup
                                         # {Session} $session to term

#--------------------------------------------------------------------#
# Variables                                                          #
#--------------------------------------------------------------------#
# Initialise the Perl extension (Class) variables used in the program
# - this cleans up many memory problems, apparently.
my $Table_handle           = "";

# These are fed into init_session
my $Tree_handle            = "";

# Here the variables needed for retrieving the file are predefined
my @Results                = ();
my @Column_names           = ();

my $Set_work_dir           = $FALSE;
my $Table                  = "";
my $rc                     = 0;          # assume good return code
my $Mount_point            = $DEFAULT_GLOBAL_MOUNT_POINT;


#--------------------------------------------------------------------#
# Main Code                                                          #
#--------------------------------------------------------------------#
# TODO: Verbose messages in this routine that aren't using message
# maps are trace statements. When trace facilities are available to
# the SR these will be changed. (feature 48401)

# TODO: security on access to the table can not be further defined
# until after a security design has been implemented for the SR.
# ( feature 48402 ) Until then, the table is opened with the
# minimum security necessary to complete the command.

# Parse the command line, exit if there are errors
# Error messages are handled in parse_cmd_line
($rc, $SR_filename, $AIX_filename) = parse_cmd_line();
($rc == 0) || error_exit($rc);  

if ($Verbose) {
    $Command_line_input = "\"  $SR_filename\n  $AIX_filename\n\"";  
    printIMsg("IMsgretsrfileCommandLineInput", $Command_line_input);
}


# Establish registry session if possible. Change current directory if 
# a relative table name given, using value set in CT_SR_HOME.
# Initialise the tree handle, blessing it into tree_handle_t class

($Set_work_dir, $Table) = set_session_variables($SR_filename);
($rc,$Tree_handle) = init_session( $Set_work_dir );
($rc == 0) || error_exit($rc);  

# Add tree handle to cleanup hash
$Cleanup{Session} = $Tree_handle;


$Verbose && 
       printIMsg("IMsgretsrfileRetrievingFile", $Table);


# Open table to be selected on. If there's an error, close
# gracefully by ending the registry session and exiting.
($rc, $Table_handle) = open_table($Tree_handle, $Table,
                        $SR_filename, SR_READ); 
($rc == 0) || error_exit($rc);  

# Add table handle to cleanup hash
push @{$Cleanup{Tables}}, $Table_handle;


# Set up columns to be retrieved - get the entire row
# if the file is to be saved in an AIX file. If not,
# just get enough data to display it to the screen.

if ($Opt_Save_Result) {
    # Grab the entries necessary to save the file to AIX

    $Column_names[0] = "RowChangeCounter";
    $Column_names[1] = "Key";
    $Column_names[2] = "FileName";
    $Column_names[3] = "ModificationDate";
    $Column_names[4] = "LastAccessDate";
    $Column_names[5] = "Permissions";
    $Column_names[6] = "UID";
    $Column_names[7] = "GID";
    $Column_names[8] = "Type";
    $Column_names[9] = "FileData";

    $Results[0] = NULL;
    $Results[1] = [(CT_UINT64, CT_UINT32, CT_CHAR_PTR, CT_CHAR_PTR, 
                CT_CHAR_PTR, CT_CHAR_PTR, CT_CHAR_PTR, 
                CT_CHAR_PTR, CT_CHAR_PTR, CT_BINARY_PTR )];
    $Results[2] = 10;

}
else {
    # Grab the entries necessary to display the file contents only  

    $Column_names[0] = "RowChangeCounter";
    $Column_names[1] = "Key";
    $Column_names[2] = "FileName";
    $Column_names[3] = "Type";
    $Column_names[4] = "FileData";

    $Results[0] = NULL;
    $Results[1] = [(CT_UINT64, CT_UINT32, 
                    CT_CHAR_PTR, CT_CHAR_PTR, CT_BINARY_PTR)];
    $Results[2] = 5;
}


$Trace && print STDERR "Calling CT::SR::get_fields_by_index\n";
($rc, $Results) = CT::SR::get_fields_by_index($Table_handle, 0, 
                        \@Column_names, \@Results);
$Trace && 
    print STDERR "CT::SR::get_fields_by_index return code $rc\n";

# Check result of previous call
# Under normal Registry Library function, all these
# error codes should be trapped before this point. If they
# are not, then there may be something seriously wrong
# with the registry, or the table may have become corrupt.
$rc = error_check("sr_get_fields_by_index", $rc, $SR_filename);
($rc == 0) || error_exit($rc);  


# Grab the contents of the FileData column. May have to
# check what the FileType column says for the unpack. TODO
if ($Opt_Save_Result) {
    $Buffer = $Results->[0][9]->{"image"};
    $Buflen = $Results->[0][9]->{"length"};
    $type = $Results->[0][8];
}
else {
    $Buffer = $Results->[0][4]->{"image"};
    $Buflen = $Results->[0][4]->{"length"};
    $type = $Results->[0][3];
}

# unpack trims off trailing newline characters, too. I'm not
# impressed with pack and unpack so far...
# TODO: until such time as this is figured out, these commands will be
# barely using the pack/unpack facility - if at all.

#if ($type eq "text") {
#    $Output = unpack("A*", $Buffer);
#}
#else { $Output =  $Buffer; }
$Output =  $Buffer; 


# Check the input flags to see how to display the data

if ($Opt_Save_Result) {

    if ($AIX_filename eq "") {
        # Grab the fully qualified file name stored with the data
        # in the System Registry
        # 'open' supports relative file names, so $AIX_filename
        # is otherwise used as it came off the command line

        $AIX_filename = $Results->[0][2];
    }


    # Check to see if the file already exists.
    if ((-e $AIX_filename) && !$Opt_Force_Save) {

        printEMsg ("EMsgretsrfileAIXFileExists", $AIX_filename);

        $rc = SR_CLI_USER_ERROR;

    }
    elsif ($Opt_Force_Save) {
        # Replacing an existing AIX file
        # Write the buffer to a temp file 

        my $temp_name = $AIX_filename.'.tmp';
        if (!open(FH, ">$temp_name"))
        {   printCEMsg("EMsgSRcliCannotOpenFile", $temp_name);
            $rc =  SR_CLI_ERROR;
        }
        else {
            print FH $Output;
            close (FH);

            # Only replace the file if the newly created size
            # is the same as the stored size.
            # An assumption is made here based on test data that the
            # value stored in $Buflen is equal to the byte count
            # given in AIX for the filename.

            if ((-s $temp_name) != $Buflen) {
                if ($Verbose)  {
                    my $temp = -s $temp_name;
                    printIMsg("IMsgretsrfileFileSizes", $temp, 
                                    $Buflen);
                }
                printEMsg("EMsgretsrfileErrorWritingAIXFile", 
                                    $AIX_filename);
                $rc = SR_CLI_ERROR; 

                # Remove the temp file
                $rc = unlink($temp_name);
                $Verbose && print STDERR "unlink return code: $rc\n";
            
            }
            else {
                # Move the file over top of the existing one.

                $rc = move($temp_name, $AIX_filename);
                $Verbose && print STDERR "return code of move: $rc\n";

                if ($Opt_Restore_Stats) {
                    $rc = restore_AIX_file_stats($AIX_filename, 
                        $Results);
                }
            }
        }

    } # end if ($Opt_Force_Save)
    else {  

        # Creating a new AIX file
        if (!open(FH, ">$AIX_filename"))
        {   printCEMsg("EMsgSRcliCannotOpenFile", $AIX_filename);
            $rc =  SR_CLI_ERROR;
        }
        else {
           print FH $Output;
           close(FH);

            if ($Opt_Restore_Stats) {
                $rc = restore_AIX_file_stats($AIX_filename, $Results);
            }           
        }

    } # end if ($Opt_Force_Save)
}
else {
    # Simply print to the screen
    ($type eq "text") ?  print "$Output\n" 
                        : printEMsg("EMsgretsrfileNoBinarytoScreen");
}


# Check error code, and exit
($rc == 0) || error_exit($rc);  

# Check the final error codes and exit as necessary. 
# TODO: feature 48397 will handle FFDC.

$rc = clean_session($Tree_handle, $Mount_point, $Table_handle);

# Clear the cleanup hash to clean_session is not run in error_exit
%Cleanup = ();
($rc == 0) || error_exit($rc);  

exit $rc;

#--------------------------------------------------------------------#
# End Main Code                                                      #
#--------------------------------------------------------------------#


#--------------------------------------------------------------------#
# restore_AIX_file_stats                                             #
#   Sets statistics on a newly restored AIX file including uid, gid  #
#   permissions, modification and access dates, as stored with       #
#   the file in the System Registry.                                 #
#   An assumption is made that this subroutine is only displayed     #
#   if a file is to be restored. It only in this case that there     #
#   will be enough information in the $stats input parameter to      #
#   successfully complete the calls in this subroutine.              #
#                                                                    #
# Parameters:                                                        #
#   $filename    - name of AIX file to be adjusted                   #
#   $stats       - data to be restored closed                        #
#                                                                    #
# Returns:                                                           #
#   $local_rc - local return code                                    #
#--------------------------------------------------------------------#
sub restore_AIX_file_stats
{
# Grab input parameters
my $filename = shift;
my $stats = shift;

# Set local variables 
my ($permissions, $mod_date, $access_date, $uid, $gid, $count);
my $local_rc = 0;

# Set the permissions as stored
$permissions = $stats->[0][5];
   
# The permissions list must be in octal. 
$count = chmod oct($permissions), $filename;
if ($count != 1) {
    printEMsg("EMsgretsrfileErrorChmod", $filename);
    return SR_CLI_ERROR;
}

# Set the modification and access dates
$mod_date = $stats->[0][3];
$access_date = $stats->[0][4];
$count = utime $access_date, $mod_date, $filename;
if ($count != 1) {
    printEMsg("EMsgretsrfileErrorUtime", $filename);
    return SR_CLI_ERROR;
}

# Set the user/group id on the file
$uid = $stats->[0][6];
$gid = $stats->[0][7];
$count = chown $uid, $gid, $filename;

# TODO: the c175 systems won't allow chgrp to run for
# some unknown reason. Until such time as it's discovered why,
# this check is edited out. The previous chown command may not
# work due to AIX restrictions?
#if ($count != 1) {
#   print "count: $count\n";
#   printEMsg("EMsgretsrfileErrorChown", $filename);
#   return SR_CLI_ERROR;
#}

return $local_rc;
}   # end restore_file_stats


#--------------------------------------------------------------------#
# parse_cmd_line:                                                    #
#   Uses getopts to collect flags and input from command line        #
#   continuing to parse any remaining data in @ARGV.                 #
#                                                                    #
# Return values:                                                     #
#   $local_rc   - local return code                                  #
#   $SR_file    - name of the table to retrieve file data from       #
#   $AIX_file   - name of the AIX file to save the file data to      #
#                                                                    #
# Global Variables Altered:                                          #
#   $Opt_Force_Save    output   True (-f) force overwriting of an    #
#                               existing AIX file.                   #
#   $Opt_Restore_Stats output   False (-n) do not restore AIX file   #
#                               stats on write.                      #
#   $Opt_Save_Result            True (-r) save the file data to an   #
#                               AIX file.                            #
#   $Trace             output   True (-T) turn Trace mode on.        #
#   $Verbose           output   True (-V) turn Verbose mode on.      #
#--------------------------------------------------------------------#
sub parse_cmd_line
{
my $local_rc    = 0;
my $SR_file     = "";
my $AIX_file    = "";
my %opts = ();

# Get input flags, if any - exit if incorrect flag given
if (getopts('hfnrTV', \%opts) == 0) {        
    printCEMsg("EMsgSRcliInvalidFlag");
    print_usage();
    return SR_CLI_BAD_FLAG;
}

# Help option given - ignore rest of command line 
if (defined $opts{h}) {             # print usage and exit
    print_usage();
    exit(0);
}

# Set Trace global if requested
if (defined $opts{T}) { 
    $Trace = $TRUE;                 # Turn trace on 
}

# Set Verbose global if requested
if (defined $opts{V}) { 
    $Verbose = $TRUE;               # Turn verbose mode on 
}

# Force saving over an existing AIX file
if (defined $opts{f}) { 
    $Opt_Force_Save = $TRUE;
}

# Do not restore the file details
if (defined $opts{n}) { 
    $Opt_Restore_Stats = $FALSE; 
}

# Save the result table   
if (defined $opts{r}) { 
    $Opt_Save_Result = $TRUE; 
}

# Grab the SR tablename, if there
if (@ARGV) { 
    $SR_file = shift @ARGV;
}
else {
    printCEMsg("EMsgSRcliNoFileNameGiven");
    print_usage();
    return SR_CLI_BAD_OPERAND;
}

# Grab the AIX file name, if there
if (@ARGV and !$Opt_Save_Result) { 
    printEMsg("EMsgretsrfileSaveFlags");
    return SR_CLI_BAD_OPERAND;
}
else {
    $AIX_file = shift @ARGV; 
}

return ($local_rc, $SR_file, $AIX_file);
}   # end parse_cmd_line


#--------------------------------------------------------------------#
# error_check:                                                       #
#   Checks the return code from the SR function.  If an error is     #
#   detected appropriate error messages will be displayed and        #
#   SR CLI return code set.                                          #
#                                                                    #
# Parameters:                                                        #
#   $sr_function  - Name of the SR function that was called and      #
#                   whose error code we are checking.                #
#   $sr_rc        - SR function return code.                         #
#   $filename     - Name of the file/table in SR.                    #
#                                                                    #
# Return values:                                                     #
#   None.                                                            #
#                                                                    #
# Global References:                                                 #
#   None.                                                            #
#--------------------------------------------------------------------#
sub error_check
{
my ($sr_function, $sr_rc, $filename) = @_;
my $rc = 0;

if ($sr_rc !=0) {
    if ($sr_rc == SR_NO_PERMISSION) {
        printCEMsg("EMsgSRcliNoPermission", $filename, $sr_function);
        $rc = SR_CLI_USER_ERROR;    
    }
    elsif ($sr_rc == SR_NO_TABLE) {
        printCEMsg("EMsgSRcliNoTable", $filename);
        $rc = SR_CLI_USER_ERROR;    
    }
    elsif ($sr_rc == SR_INVALID_ROW_INDEX) {
        printCEMsg("EMsgSRcliInvalidRowIndex", 0);
        $rc = SR_CLI_ERROR; 
    }
    elsif ($sr_rc == SR_NO_COLUMN) {
        printCEMsg("EMsgSRcliNoColumn");
        $rc = SR_CLI_ERROR; 
    }
    else {
        printCEMsg("EMsgSRcliSRCommandFailure", $sr_function, $sr_rc);
        $rc = SR_CLI_REGISTRY_ERROR
    }
}

return ($rc);
}   # end error_check


#--------------------------------------------------------------------#
# print_usage : print the usage statement (syntax) to stdout.        #
#   See this command's prologue syntax section for current usage.    #
#--------------------------------------------------------------------#
sub print_usage
{
printIMsg("IMsgretsrfileUsage");
}   # end print_usage


#--------------------------------------------------------------------#
# End File                                                           #
#--------------------------------------------------------------------#
